﻿using SIR.Common;
using SIR.Common.Condition;
using SIR.Entities.Implementations;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;

namespace SIR.Web.API
{
	public class ImageController : ApiController
	{
		/// <summary>
		/// Handles the upload of the images being posted to the image controller
		/// </summary>
		/// <returns>The HTTP Response Message</returns>
		public async Task<HttpResponseMessage> PostFormData()
		{
			// Check if the request contains multipart/form-data.
			if (!Request.Content.IsMimeMultipartContent())
			{
				// If not, fail the upload
				return Request.CreateResponse(HttpStatusCode.OK, new List<Response<Image>>() { 
					Response<Image>.CreateResponse(false, null, "Unsupported Media Type")
				});
			}

			long? userId = null;
			// Find the user from the session
			SessionManager.GetSession(HttpContext.Current, out userId);

			var responses = new List<Response<Image>>();

			try
			{
				// Read the form data.
				var provider = await Request.Content.ReadAsMultipartAsync<MultipartMemoryStreamProvider>(new MultipartMemoryStreamProvider());
				var contents = provider.Contents;
				long folderId = 0;
				HttpContent folderContent = null;
				var priority = ProcessingPriority.Normal;
				bool isPrivate = false;

				var uploadedFiles = new List<FileUpload>();
				// For each piece of data in the upload
				foreach (var content in contents)
				{
					// If there is content but it's not an image, then fail that content
					if (content.Headers.ContentType != null && !content.Headers.ContentType.MediaType.Contains("image/"))
					{
						responses.Add(Response<Image>.CreateResponse(false, null, "Unsupported Media Type"));
						continue;
					}
					// Check the disposition
					switch (content.Headers.ContentDisposition.Name.ToLower())
					{
						case "\"file\"":
							// If file, add a new filde upload reference
							uploadedFiles.Add(new FileUpload()
							{
								Content = content,
								Type = content.Headers.ContentType.MediaType.Replace("image/", string.Empty),
								Name = Path.GetFileNameWithoutExtension(content.Headers.ContentDisposition.FileName.Replace("\"", string.Empty))
							});
							break;
						case "\"folder\"":
							folderContent = content; // Get the folder
							break;
						case "\"search\"":
							priority = ProcessingPriority.Search; // When searching, set priority to search
							break;
						case "\"private\"":
							isPrivate = true; // Set to private
							break;
						default:
							// All others are unsupported
							responses.Add(Response<Image>.CreateResponse(false, null, "Unsupported Media Type"));
							break;
					}
				}

				// Try to get the folder Id
				if (folderContent != null)
				{
					// Parse the folder Id from the content
					if (!long.TryParse(folderContent.ReadAsStringAsync().Result, out folderId))
					{
						folderId = 0;
					}
					else if (!userId.HasValue)
					{
						// If a folder was specified but there was no user, fail the upload
						return Request.CreateResponse(HttpStatusCode.OK, new List<Response<Image>>() { 
							Response<Image>.CreateResponse(false, null, "Must be authenticated to upload an image to a folder")
						});
					}
				}

				// for each file found in the upload content
				foreach (var file in uploadedFiles)
				{
					// Read the bytes from the content stream
					var stream = file.Content.ReadAsStreamAsync().Result;
					var bytes = GetBinaryDataFromStream(stream);
					// Save the new image
					responses.Add(SaveImage(userId, folderId, file.Name, file.Type, stream.Length, bytes, isPrivate, priority));
				}
				// Return the responses
				return Request.CreateResponse(HttpStatusCode.OK, responses);
			}
			catch (System.Exception e)
			{
				// An exception occured 
				return Request.CreateResponse(HttpStatusCode.OK, new List<Response<Image>>() { 
					Response<Image>.CreateResponse(false, null, e.Message)
				});
			}
		}
		/// <summary>
		/// SAves the image to the system
		/// </summary>
		/// <param name="userId">The identifier for the user to be associated to the image</param>
		/// <param name="folderId">The folder identifier for the destination of the image</param>
		/// <param name="fileName">The name of the file from the user's system</param>
		/// <param name="fileType">The type of file being uploaded</param>
		/// <param name="length">The size of the file</param>
		/// <param name="bytes">The binary data</param>
		/// <param name="isPrivate">Indicates whether or not the image should be private</param>
		/// <param name="priority">The priority of the image for processing</param>
		/// <returns></returns>
		private static Response<Image> SaveImage(long? userId, long folderId, string fileName,
			string fileType, long length, byte[] bytes, bool isPrivate, ProcessingPriority priority)
		{
			Image image;
			string errorMessage;
			// Save the image with the resolved name
			var wasSuccessful = Image.SaveImage(userId, folderId,
				NameResolver.Resolve(typeof(Image), userId.HasValue ? userId.Value : -1, 0, folderId, fileName),
				fileType, length, bytes, isPrivate, priority, out image, out errorMessage);
			// Return the create image response
			return Response<Image>.CreateResponse(wasSuccessful, image, errorMessage);
		}
		/// <summary>
		/// Gets a byte array from the IO Stream
		/// </summary>
		/// <param name="stream">The IO stream containing btye data</param>
		/// <returns>A byte array from the stream</returns>
		private static byte[] GetBinaryDataFromStream(System.IO.Stream stream)
		{
			// Create a byte area and populate it from the stream
			var bytes = new byte[stream.Length];
			stream.Read(bytes, 0, (int)stream.Length);
			return bytes;
		}
		/// <summary>
		/// Returns an HTTP response with the data of the specified image
		/// </summary>
		/// <param name="id">The identifier for the image</param>
		/// <returns>A response containing the image</returns>
		public HttpResponseMessage Get(long id)
		{
			byte[] data = new byte[0];

			long? userId = null;
			// Get the user session
			SessionManager.GetSession(HttpContext.Current, out userId);
			// Find the image from the Id
			var image = DAL.DataServer.Find<Image>(new PredicateGroup(new IPredicate[] 
			{
				new PredicateGroup(new IPredicate[] {
					// Match the user id or if the image is not private and folder not private
					new Predicate("UserId", userId.HasValue ? userId : -1, PredicateOperator.EqualTo),
					new PredicateGroup(new [] 
					{
						new Predicate("IsPrivate", false, PredicateOperator.EqualTo),
						new Predicate("IsFolderPrivate", false, PredicateOperator.EqualTo)
					}, true)
				}, false),
				new Predicate("Id", id, PredicateOperator.EqualTo),
			}, true)).Results.FirstOrDefault();


			// Get the image data
			if (image != null && image.Data != null)
				data = image.Data.Data;
			// Return the response with the image data
			var result = new HttpResponseMessage(HttpStatusCode.OK);
			result.Content = new ByteArrayContent(data);
			return result;
		}
		/// <summary>
		/// Returns an HTTP response with the thumbnail data of the specified image
		/// </summary>
		/// <param name="id">The identifier for the image</param>
		/// <returns>A response containing the image</returns>
		public HttpResponseMessage GetThumb(long id)
		{
			byte[] data = new byte[0];

			long? userId = null;
			// Get the user session
			SessionManager.GetSession(HttpContext.Current, out userId);
			// Find the image from the Id
			var image = DAL.DataServer.Find<Image>(new PredicateGroup(new IPredicate[] 
			{
				new PredicateGroup(new IPredicate[] {
					// Match the user id or if the image is not private and folder not private
					new Predicate("UserId", userId.HasValue ? userId : -1, PredicateOperator.EqualTo),
					new PredicateGroup(new [] 
					{
						new Predicate("IsPrivate", false, PredicateOperator.EqualTo),
						new Predicate("IsFolderPrivate", false, PredicateOperator.EqualTo)
					}, true)
				}, false),
				new Predicate("Id", id, PredicateOperator.EqualTo),
			}, true)).Results.FirstOrDefault();


			// Get the image data
			if (image != null && image.Data != null)
				data = image.Thumb.Data;
			// Return the response with the image data
			var result = new HttpResponseMessage(HttpStatusCode.OK);
			result.Content = new ByteArrayContent(data);
			return result;
		}
	}
	/// <summary>
	/// A temporary object to group file information from the upload content
	/// </summary>
	public class FileUpload
	{
		/// <summary>
		/// Gets or sets the name of the file
		/// </summary>
		public string Name { get; set; }
		/// <summary>
		/// Gets or sets the type of the file being uploaded
		/// </summary>
		public string Type { get; set; }
		/// <summary>
		/// Gets or sets the HTTP content
		/// </summary>
		public HttpContent Content { get; set; }
	}
}
